/**
 * Classe que implementa o analisador Léxico
 * @param
 * 		texto-fonte
 * @return
 * 		token
 */
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.io.*;

import Enumerators.*;

public class LexicalAnalyzer {
	Map<String, Keys> keysMap;
	ArrayList<String> numsCharsMap;
	ArrayList<String> charMap;
	Map<Integer, String> symbolTable;
	String returnedStringToken ="";
	boolean returnedToken = false;
	
	private static final String TOKENIZERS = " ;()[]{}><+/-*\""+"\t"/*tabulation*/;
	
	private StringTokenizer separador;
	
	public static String readSourceCode (String fileName){
		BufferedReader in;
		in = null;
        FileReader fis;
		String SourceAsString ="";
		String line;
	
		try {
			fis = new FileReader (fileName);
			in = new BufferedReader (fis);

			while((line = in.readLine()) != null)				
				SourceAsString +=" "+line; 

		} catch (IOException e) {
			e.printStackTrace();
		}
		return SourceAsString;
	}
		
	public LexicalAnalyzer(String fileName) {
		String stringToAnalyze = readSourceCode(fileName);

		keysMap = new HashMap<String, Keys>();
		numsCharsMap = new ArrayList<String>();
		symbolTable = new HashMap<Integer, String>();
		
		separador = new StringTokenizer( stringToAnalyze, TOKENIZERS, true );		
		
		
		for (int i=0;i<10;i++)
			numsCharsMap.add(i+"");

		numsCharsMap.add(".");
		
		//inserindo todas as Keys no KeysMap
		for (Keys i : Keys.values()) keysMap.put(i.getName(), i);
		
	
	}
	public void returnToken(String token){
		this.returnedToken = true;
		this.returnedStringToken = token;
	}
	
	public Token nextToken()
	{
		Token objectToken = new Token();
		String stringToken = "";
		boolean comment = false;
		boolean space = false;
		
		if(this.returnedToken){
			stringToken = this.returnedStringToken;
			this.returnedToken =false;
		}else
			//Tratando /*comentário*/ e espaços em branco (" " e "\t")
			do{
				stringToken = separador.nextToken();
				
				if(stringToken.length() == 1 && ((stringToken.compareTo(" ") == 0) ||(stringToken.compareTo("\t") == 0))){
					space = true;
				}
				else {
						space = false;
						if(stringToken.length() > 1)
							if (stringToken.substring(0, 2).equals("/*"))
								comment = true;
							else if(comment && stringToken.substring(0, 2).equals("*/")){
								comment = false;
								space = true; // gambi para forçar iteração
							}
				}
				
			}while ( comment || space);
		
		//Descobrindo o tipo do Token
		objectToken.setType(tokenType(stringToken));

		//Tratando Token String
		if(objectToken.getType() == TokenType.STRING)
		{
			stringToken = stringToken + separador.nextToken();
			while(! stringToken.endsWith("\""))
			{
				stringToken = stringToken + separador.nextToken();
			}
            stringToken = stringToken.substring(1, stringToken.length()-1);
		}
		objectToken.setValue(stringToken);	
		// colocar na tabela de simbolos os Identificadores e os numeros
		// caso forem outros tipos de tokens,o id sera -1
		if (objectToken.getType() == TokenType.ID || objectToken.getType() == TokenType.NUM){
			objectToken.setId(this.checkSymbol(objectToken));
		}else
			objectToken.setId(-1);
		
		return objectToken;
	}
	
	public boolean hasMoreTokens()
	{
		if(this.returnedToken){
			return true;
		}else		
		return separador.hasMoreTokens() ;
	}
	
	private TokenType tokenType(String token)
	{
		TokenType type = TokenType.STRING;	

		if(token.startsWith("\""))
		{
			type = TokenType.STRING;		
		}
		else if(isNumeric(token)) 
		{
			type = TokenType.NUM;
		}
		else if(keysMap.containsKey(token))
		{
			type = TokenType.KEYWORD;
		}
		else 
		{
			type = TokenType.ID;
		}		
		
		return type;
	}
	
	private boolean isNumeric(String text)
	{
		boolean isNumeric = true;
		
		for(int i=0;i < text.length(); i++)
		{
			if(! numsCharsMap.contains(String.valueOf(text.charAt(i))))
			{
				isNumeric = false;
			}
		}
		
		return isNumeric;
	}
	
	/*
	 * Verifica a existencia do identificador na tabela de simbolos
	 *  caso o simbolo nao exista: insere na tabela e retorna o indice
	 *  caso contrario, retorna o indice do mesmo
	 */
	public int checkSymbol(Token token){
		int index;
			String identifier = token.getValue();
			for (index =0 ; index < this.symbolTable.size() ; index++ ){
				if(this.symbolTable.get(index).equals(identifier)){return index;}
			}
			this.symbolTable.put(index, identifier);
			return index;
	}
	
	public Map<Integer, String> getSymbolTable(){
		
		return this.symbolTable;
	}
	
	public static void main(String[] args) {
		
		LexicalAnalyzer analyzer = new LexicalAnalyzer("helloWorld.petros");

		
		while(analyzer.hasMoreTokens()){
			Token token = analyzer.nextToken();	
				
			if (token.getId() != -1){
				System.out.println("TYPE:["+token.getType().name() + "]\tVALUE:["+token.getValue()+"]\tSymbolTableId["+token.getId()+"]");
			}else
				System.out.println("TYPE:["+token.getType().name() + "]\tVALUE:["+token.getValue()+"]");	
		}		
	}
	
}
